Skip to content

fix: pin rustls to a single crypto provider preventing panics - #543

Closed
surpher wants to merge 1 commit into
pact-foundation:masterfrom
surpher:fix/rustls-single-crypto-provider
Closed

fix: pin rustls to a single crypto provider preventing panics#543
surpher wants to merge 1 commit into
pact-foundation:masterfrom
surpher:fix/rustls-single-crypto-provider

Conversation

@surpher

@surpher surpher commented Jul 31, 2026

Copy link
Copy Markdown

disclaimer: The following PR description was prepared with help of Claude AI.

Before accepting these suggested changes, verify it doesn't break other libpact_ffi users.

Problem

Starting a TLS mock server panics and then aborts the host process. rustls cannot pick a crypto provider because both of its backend features are enabled:

thread '<unnamed>' panicked at rustls-0.23.43/src/crypto/mod.rs:249:14:
Could not automatically determine the process-level CryptoProvider from Rustls crate features.
Call CryptoProvider::install_default() before this point to select a provider manually, or make
sure exactly one of the 'aws-lc-rs' and 'ring' features is enabled.

The panic then poisons a lock inside pact_ffi, and the next pactffi_free_pact_handle unwraps the PoisonError from a context that cannot unwind, which turns a recoverable error into a hard abort:

thread '<unnamed>' panicked at pact_ffi/src/mock_server/handles.rs:3271:41:
called `Result::unwrap()` on an `Err` value: PoisonError { .. }
thread caused non-unwinding panic. aborting.

For an FFI consumer this is fatal rather than merely a failed test: the whole test host dies, taking unrelated tests with it. It reproduces through the FFI on libpact_ffi-v0.5.5 (and v0.5.4) on aarch64-apple-darwin via pactffi_create_mock_server_for_transport with an HTTPS transport, and equally via pactffi_get_tls_ca_certificate.

Root cause

Both providers get enabled by feature unification, because the workspace straddles two reqwest majors that default to different backends:

Crate reqwest Feature requested Provider it forces
pact_matching, pact_models 0.12.28 rustls-tls-native-roots __rustls-ringring
pact_ffi, pact_verifier, pact_consumer 0.13 rustls __rustls-aws-lc-rsaws-lc-rs

pact_mock_server 2.2.4 — which implements the TLS mock server — also depends on rustls, tokio-rustls and hyper-rustls with ring directly. Cargo unions all of it, so rustls ends up with both:

# cargo tree --edges no-dev --format "{f}" -p rustls
rustls v0.23.43 :: aws-lc-rs,aws_lc_rs,log,logging,ring,std,tls12

In reqwest 0.13, the rustls feature hard-wires the backend, whereas rustls-no-provider is the same feature set minus the forced backend:

rustls             = ["__rustls-aws-lc-rs", "dep:rustls-platform-verifier", "__rustls"]
rustls-no-provider = [                      "dep:rustls-platform-verifier", "__rustls"]

Change

Switch the three reqwest 0.13 dependencies from rustls to rustls-no-provider, leaving ring — already required by pact_mock_server — as the single provider:

  • pact_ffi/Cargo.toml
  • pact_verifier/Cargo.toml
  • pact_consumer/Cargo.toml

Because the two features differ only in the forced backend, rustls-platform-verifier stays enabled and certificate verification behaviour is unchanged.

Result:

# cargo tree --edges no-dev --format "{f}" -p rustls
rustls v0.23.43 :: log,logging,ring,std,tls12

aws-lc disappears from the pact_ffi dependency graph entirely, which also drops the aws-lc-sys C build. Cargo.lock loses exactly two dependency edges (aws-lc-rs from quinn, quinn from reqwest); no packages are added or removed.

Verification

Built pact_ffi for aarch64-apple-darwin, aarch64-apple-ios and aarch64-apple-ios-sim, then ran the full test suite of a Swift FFI consumer (PactSwiftMockServer) against the resulting static libraries:

  • Before: the two TLS mock server tests never complete — 2 CryptoProvider panics and 2 aborts per run.
  • After: 54 passed / 0 failed on both macOS and iOS, 0 panics, 0 aborts. The previously fatal pactffi_get_tls_ca_certificate and HTTPS-transport mock server cases now pass.
  • Confirmed nm reports no aws_lc symbols and ring_core symbols present in all three archives.

I have not run the pact-reference Rust test suite itself — worth a CI run, particularly anything covering pact_verifier's HTTPS client, since that is the code path whose provider selection now comes from the process-level default rather than from reqwest's feature.

Test logs after applying changes and re-building libpact_ffi binaries to use with PactSwiftMockServer:
pact-swift-mock-server_tests.log

@rholshausen

Copy link
Copy Markdown
Contributor

Leave this one with me, it looks like it breaks things. I've had lots of issues with aws-lc-sys (just as they sorted out all the issues with ring someone decided we needed new issues). Didn't know about rustls-no-provider

@rholshausen

Copy link
Copy Markdown
Contributor

I have released FFI 0.5.6 which hopefully fixes this issue. I'll close this PR.

@rholshausen rholshausen closed this Aug 5, 2026
@mefellows

Copy link
Copy Markdown
Member

Awesome, thanks Ron

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants